package gicom.client;

import gicom.generated.bankServices.Account;
import gicom.generated.bankServices.Address;
import gicom.generated.bankServices.Bank;
import gicom.generated.bankServices.BankHelper;
import gicom.generated.bankServices.Branch;
import gicom.generated.bankServices.BranchStruct;
import gicom.generated.bankServices.Customer;
import gicom.generated.bankServices.Gender;
import gicom.generated.bankServices.ProblemException;
import gicom.generated.transaction.GlobalTransactionManager;
import gicom.generated.transaction.LocalTransactionManager;

import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Properties;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.omg.CORBA.ORB;
import org.omg.CORBA.Object;
import org.omg.CORBA.ORBPackage.InvalidName;
import org.omg.CosNaming.Binding;
import org.omg.CosNaming.BindingIteratorHolder;
import org.omg.CosNaming.BindingListHolder;
import org.omg.CosNaming.BindingType;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;
import org.omg.CosNaming.NamingContextPackage.CannotProceed;
import org.omg.CosNaming.NamingContextPackage.NotFound;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.POAHelper;
import org.omg.PortableServer.POAManagerPackage.AdapterInactive;

import com.sun.corba.se.impl.orbutil.ORBConstants;

public class BankClient {

    /**
     * Branch selected by the user
     */
    private static Branch curBranch = null;
    /**
     * Customer selected by the user
     */
    private static Customer curCustomer = null;

    public static GlobalTransactionManager globalTM;
    public static LocalTransactionManager localTM;

    private static ORB orb;
    private static POA poa;
    private static POA persistentpoa;

    public static void main(String[] args) {

	// Map of all available banks
	LinkedHashMap<String, Bank> bankMap = new LinkedHashMap<String, Bank>();

	// Map of all available branches for the selected bank
	LinkedHashMap<String, Branch> branchMap = null;

	// Bank selected by the user
	Bank curBank = null;

	// String used to get the user's input
	String userInput;

	System.out.println("--- DEMARRAGE de BankClient ---");
	try {
	    
	    initAll(args);

	    curBank = selectBank(args, bankMap, branchMap, curBank);

	    // curBank.desactivate();

	    System.out.println(curBank == null);

	    // Get and print curBank's branches
	    System.out.println(curBank.name() + " branches");
	    System.out.println("----------------------------");
	    branchMap = listBranches(curBank);

	    if (branchMap == null || branchMap.isEmpty()) {
		curBank = selectBank(args, bankMap, branchMap, curBank);
	    }

	    // Branch selection
	    do {
		System.out.println("\nSelect a branch :");
		userInput = getStringInput();
		curBranch = branchMap.get(userInput);
		if (curBranch == null) {
		    System.out.println("Invalid input.");
		}

	    } while (curBranch == null);

	    // curBranch.desactivate();

	    localTM = curBranch.localTM();
	    globalTM = localTM.globalTM();

//	    Customer cust1 = curBranch.createCustomer("123", "Renault", Gender.male, new Address("11 rue", "Grenoble", "01234567"));
//	    Customer cust2 = curBranch.createCustomer("456", "Missonnier", Gender.male, new Address("11 avenue", "Grenoble", "0987654321"));

//	    cust1.createAccount("321", 10000);
//	    cust2.createAccount("654", 30000);
	    
	    // Branch mgmt
	    manageBranch();

	} catch (Exception e) {
	    e.printStackTrace();
	}
    }

    private static void manageAccount(Account curAccount) {

	float amount = 0;
	// Operation chosen by the user
	int choix = 0;
	// choix == 4 => Exit
	while (choix != 4) {

	    System.out.println("1 : Print balance");
	    System.out.println("2 : Withdraw");
	    System.out.println("3 : Deposit");
	    System.out.println("4 : Exit\n>");
	    choix = getIntInput();

	    switch (choix) {
	    case 0:
		printBranchInfos();
		break;
	    case 1:
		System.out.println("\t Balance = " + curAccount.balance());
		break;
	    case 2:
		System.out.println("Please enter the amount to withdraw : ");
		amount = getFloatInput();
		try {
		    curAccount.debit(amount);
		    /*
		     * int id = globalTM.begin(); localTM.debit(id, amount ,
		     * curCustomer.number(), curAccount.number());
		     * localTM.credit(id, amount, curCustomer.number(),
		     * "234567"); globalTM.commit(id);
		     */
		} catch (Exception ex) {
		    // System.err.println(ex.matter + " : " + ex.reason);
		    System.err.println(ex.getMessage());
		    // Logger.getLogger(BankClient.class.getName()).log(Level.SEVERE,
		    // null, ex);
		    break;
		}
		System.out.println("Withdrew " + amount);
		break;
	    case 3:
		System.out.println("Please enter the amount to deposit : ");
		amount = getFloatInput();
		try {
		    curAccount.credit(amount);
		} catch (ProblemException ex) {
		    System.err.println(ex.matter + " : " + ex.reason);
		    // Logger.getLogger(BankClient.class.getName()).log(Level.SEVERE,
		    // null, ex);
		    break;
		}
		System.out.println("Deposited " + amount);
		break;
	    }

	}

    }

    /**
     * Prints available operations on curBranch and executes the user's choice
     */
    private static void manageBranch() {
	// Operation chosen by the user
	int choix = 0;

	// Strings used to get the user's inputs
	String number;
	String name;
	String street;
	String city;
	String phone;

	/*
	 * try { curBranch.createCustomer("123", "renault", Gender.male, new
	 * Address("11 rue pasteur","Grenoble","1234567890")); } catch
	 * (ProblemException e1) { // TODO Auto-generated catch block
	 * e1.printStackTrace(); }
	 */

	// choix == 5 => Exit
	while (choix != 5) {
	    System.out.println("0 : Branch infos");
	    System.out.println("1 : Customer list");
	    System.out.println("2 : Lookup a customer");
	    System.out.println("3 : Add a customer");
	    System.out.println("4 : Manage a customer");
	    System.out.println("5 : Exit\n>");
	    choix = getIntInput();
	    switch (choix) {
	    case (0):
		printBranchInfos();
		break;
	    case (1):
		printCustomerList();
		break;
	    case (2):
		System.out.println("Please enter a customer number : ");
		number = getStringInput();
		searchCustomer(number);
		break;
	    case (3):
		String gen = "0";
		Customer cust = null;

		System.out.println("Please enter : ");
		do {
		    System.out.println("\tthe customer's Number : ");
		    number = getStringInput();

		    // verify that this number is not already taken
		    try {
			cust = curBranch.findCustomer(number);
		    } catch (ProblemException e) {
			// Here ProblemException means that no customer was
			// found
			cust = null;
		    }
		    if (cust != null) {
			System.out.println("Invalid value (this number is already in use)");
		    }
		} while (cust != null);

		System.out.println("\tthe customer's Name : ");
		name = getStringInput();

		do {
		    System.out.println("\tthe customer's Gender ('M' or 'F') : ");
		    gen = getStringInput();
		    // Verify that the user entered either "M" or "F"
		    if (!gen.toLowerCase().equals("m") && !gen.toLowerCase().equals("f")) {
			System.out.println("Invalid value (Please enter 'M' or 'F')");
		    }
		} while (!gen.toLowerCase().equals("m") && !gen.toLowerCase().equals("f"));

		System.out.println("\tthe customer's Street Address : ");
		street = getStringInput();
		System.out.println("\tthe customer's City : ");
		city = getStringInput();
		System.out.println("\tthe customer's Phone Number : ");
		phone = getStringInput();

		addCustomer(number, name, gen, street, city, phone);
		break;

	    case (4):
		System.out.println("\tWhich customer do you want to manage ? ");
		String userInput = null;
		do {
		    try {
			userInput = getStringInput();
			curCustomer = curBranch.findCustomer(userInput);
			manageCustomer();
		    } catch (Exception e) {
			System.err.println(e.toString());
			continue;
		    }
		} while (curCustomer == null || userInput.equals("exit"));
	    }

	}

    }

    /**
     * Creates a Gender object and creates a customer.
     * 
     * @param number
     *                the customer's ID number (unique, for his branch at least)
     * @param name
     *                the customer's name
     * @param gen
     *                the customer's gender ("M" or "F")
     * @param street
     *                the customer's street address
     * @param city
     *                the customer's city
     * @param phone
     *                the customer's phone number
     */
    private static void addCustomer(String number, String name, String gen, String street, String city, String phone) {
	Address addr = new Address(street, city, phone);
	Gender g;

	// Instantiate the Gender
	if (gen.toLowerCase().equals("m")) {
	    g = Gender.male;
	} else {
	    g = Gender.female;
	}

	// Try to instantiate the Customer. Should not fail because we checked
	// that the number isn't already taken
	try {
	    curBranch.createCustomer(number, name, g, addr);
	} catch (ProblemException e) {
	    System.err.println(e.matter + " : " + e.reason);
	}
    }

    private static void printAccountList() {
	Account[] res = null;
	try {

	    res = curCustomer.findAllAccounts();
	} catch (ProblemException ex) {
	    ex.printStackTrace();
	    Logger.getLogger(BankClient.class.getName()).log(Level.SEVERE, null, ex);
	}
	if (res != null) {
	    if (res.length == 0) {
		System.out.println("This customer does not have any accounts.");
	    } else {
		for (int i = 0; i < res.length; i++) {
		    System.out.println(res[i].number() + " : " + res[i].balance());
		}
	    }
	}
    }

    /**
     * Prints curBranch's customer list
     */
    private static void printCustomerList() {
	Customer[] res = curBranch.findAllCustomers();
	if (res.length == 0) {
	    System.out.println("This branch does not have any customer.");
	} else {
	    for (int i = 0; i < res.length; i++) {
		System.out.println(res[i].number() + " : " + res[i].name());
	    }
	}
    }

    /**
     * Searches for a customer of curBranch, and prints the result
     * 
     * @param number
     *                Customer's number
     */
    private static void searchCustomer(String number) {
	Customer cust = null;
	try {
	    cust = curBranch.findCustomer(number);
	} catch (ProblemException e) {
	    System.err.println(e.matter + " : " + e.reason);
	    e.printStackTrace();
	}
	if (cust != null) {
	    System.out.println(cust.number() + " : " + cust.name());
	}
    }

    /**
     * Prints curBranch's infos
     */
    private static void printBranchInfos() {
	System.out.println("Number\t:\t" + curBranch.number());
	System.out.println("Name\t:\t" + curBranch.name());
	System.out.println("Bank\t:\t" + curBranch.refBank().name());
	System.out.println("Address\t:\t" + curBranch.addr().street + ", " + curBranch.addr().city);
	System.out.println("Phone number:\t" + curBranch.addr().telephone);
	System.out.println();
    }

    /**
     * Scanner.nextLine() wrapper
     * 
     * @return the String entered by the user
     */
    private static String getStringInput() {
	return (new Scanner(System.in)).nextLine();
    }

    /**
     * Scanner.nextInt() wrapper
     * 
     * @return the int entered by the user
     */
    private static int getIntInput() {
	return (new Scanner(System.in)).nextInt();
    }

    /**
     * Scanner.nextFloat() wrapper
     * 
     * @return the float entered by the user
     */
    private static float getFloatInput() {
	return (new Scanner(System.in)).nextFloat();
    }

    /**
     * Gets bank's branches, prints their number and name, and returns a hashmap
     * of them
     * 
     * @param bank
     *                The bank...
     * @return A map of <branch number, Branch>
     * @throws ProblemException
     *                 when findAllBranches failed
     */
    private static LinkedHashMap<String, Branch> listBranches(Bank bank) throws ProblemException {
	LinkedHashMap<String, Branch> res = new LinkedHashMap<String, Branch>();
	BranchStruct[] branches = bank.findAllBranches();

	for (int i = 0; i < branches.length; i++) {
	    res.put(branches[i].number, branches[i].refBranch);
	    System.out.println(branches[i].number + " : " + branches[i].refBranch.name());
	}

	return res;
    }

    private static boolean initAll(String[] args) {
	try {
	    orb = org.omg.CORBA.ORB.init(args, null);
	    NamingContextExt nc = NamingContextExtHelper.narrow(orb.resolve_initial_references("NameService"));
	    
	    BankHelper.narrow(nc.resolve(nc.to_name("LaPoste"))).findBranch("Lyon").createCustomer("123", "Renault", Gender.male, new Address("11 rue", "Grenoble", "01234567")).createAccount("321", 10000);
	    BankHelper.narrow(nc.resolve(nc.to_name("LaPoste"))).findBranch("Grenoble").createCustomer("456", "Missonnier", Gender.male, new Address("11 avenue", "Grenoble", "01234567")).createAccount("654", 10000);
	    BankHelper.narrow(nc.resolve(nc.to_name("CreditMutuel"))).findBranch("Rennes").createCustomer("789", "Kolb", Gender.male, new Address("11 place", "Grenoble", "01234567")).createAccount("987", 10000);
	    BankHelper.narrow(nc.resolve(nc.to_name("CreditMutuel"))).findBranch("Le Mans").createCustomer("012", "USERA", Gender.male, new Address("11 Square", "Grenoble", "01234567")).createAccount("210", 10000);
	} catch (Exception e) {
	    // TODO: handle exception
	    e.printStackTrace();
	}
	return false;
    }

    private static Bank selectBank(String[] args, LinkedHashMap<String, Bank> bankMap, LinkedHashMap<String, Branch> branchMap, Bank curBank)
	    throws InvalidName, NotFound, CannotProceed, org.omg.CosNaming.NamingContextPackage.InvalidName {
	String userInput;
	Properties prop = new Properties();
	prop.setProperty(ORBConstants.INITIAL_HOST_PROPERTY, "localhost");
	prop.setProperty(ORBConstants.INITIAL_PORT_PROPERTY, "4711");
	prop.setProperty(ORBConstants.SERVER_HOST_PROPERTY, "localhost");
	// prop.setProperty(ORBConstants.PERSISTENT_SERVER_PORT_PROPERTY, "");
	// orb = org.omg.CORBA.ORB.init(new String[0], prop);
	// orb = org.omg.CORBA.ORB.init(args, null);
	org.omg.CORBA.Object obr = orb.resolve_initial_references("RootPOA");
	poa = POAHelper.narrow(obr);
	/*
	 * String[] list = orb.list_initial_services(); for (int i = 0; i <
	 * list.length; i++) { System.out.println(list[i]); }
	 */

	try {
	    poa.the_POAManager().activate();
	} catch (AdapterInactive e) {
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	}

	/*
	 * try { persistentpoa = poa.find_POA("persistentpoa", true); } catch
	 * (AdapterNonExistent e) { // TODO Auto-generated catch block
	 * e.printStackTrace(); }
	 */

	// get hold of the naming service
	NamingContextExt nc;

	nc = NamingContextExtHelper.narrow(orb.resolve_initial_references("NameService"));

	BindingListHolder bl = new BindingListHolder();
	BindingIteratorHolder blIt = new BindingIteratorHolder();
	nc.list(1000, bl, blIt);

	Binding bindings[] = bl.value;
	if (bindings.length == 0) {
	    return null;
	}

	System.out.println("Available banks");
	System.out.println("---------------");

	for (int i = 0; i < bindings.length; i++) {
	    // get the object reference for each binding
	    int lastIx = bindings[i].binding_name.length - 1;
	    // check to see if this is not a naming context
	    if (bindings[i].binding_type != BindingType.ncontext) {
		// Get object reference
		Object o = nc.resolve(nc.to_name(bindings[i].binding_name[lastIx].id));
		// If o is a bank, then we print it to the screen, and keep
		// a reference to it
		if (o instanceof Bank) {
		    Bank s = BankHelper.narrow(o);
		    System.out.println(s.number() + " : " + s.name());
		    bankMap.put(s.number(), s);
		}
	    }
	}

	// Bank selection
	do {
	    System.out.println("\nSelect a bank : ");
	    userInput = getStringInput();

	    curBank = bankMap.get(userInput);
	    if (curBank == null) {
		System.err.println("Invalid input.");
	    }
	} while (curBank == null);

	return curBank;
    }

    private static void manageCustomer() {
	int choix = 0;

	String number = null;
	float balance = 1000;
	Account curAccount;
	/*
	 * try { curAccount = curCustomer.createAccount("123456", balance);
	 * curAccount = curCustomer.createAccount("234567", balance * 2); }
	 * catch (ProblemException e1) { // TODO Auto-generated catch block
	 * e1.printStackTrace(); }
	 */

	System.out.println("\n\n---------------\n" + "Manage Customer" + "\n---------------");

	while (choix != 5) {

	    try {

		Process p = Runtime.getRuntime().exec("cls etc");
	    } catch (IOException ex) {
		// Logger.getLogger(BankClient.class.getName()).log(Level.SEVERE,
		// null, ex);
	    }

	    System.out.println("0 : Customer infos");
	    System.out.println("1 : Account list");
	    System.out.println("2 : Lookup an Account");
	    System.out.println("3 : Create an Account");
	    System.out.println("4 : Manage an Account");
	    System.out.println("5 : Exit\n>");
	    choix = getIntInput();
	    switch (choix) {
	    case 0:
		System.out.println("\tCustomer number : " + curCustomer.number());
		System.out.println("\tname : " + curCustomer.name() + "\n");
		break;
	    case 1:
		printAccountList();
		break;
	    case 3:
		System.out.println("\tnumber : ");
		number = getStringInput();
		System.out.println("\tbalance : ");
		balance = getFloatInput();
		try {
		    curCustomer.createAccount(number, balance);
		} catch (Exception e) {
		    // TODO Auto-generated catch block
		    e.printStackTrace();
		}
		break;
	    case 4:

		System.out.println("\tAccount Number : ");
		try {
		    curAccount = curCustomer.findAccountByPrimaryKey(getStringInput());
		} catch (ProblemException ex) {
		    System.err.println(ex.matter + " : " + ex.reason);
		    break;
		}
		manageAccount(curAccount);
		break;
	    default:
		break;
	    }

	}
    }
}
